1 /*
2 * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package sun.net.spi;
27
28 import java.net.InetSocketAddress;
29 import java.net.Proxy;
30 import java.net.ProxySelector;
31 import java.net.SocketAddress;
32 import java.net.URI;
33 import java.util.ArrayList;
34 import java.util.List;
35 import java.util.StringTokenizer;
36 import java.io.IOException;
37 import sun.misc.RegexpPool;
38 import java.security.AccessController;
39 import java.security.PrivilegedAction;
40 import sun.net.NetProperties;
41 import sun.net.SocksProxy;
42
43 /**
44 * Supports proxy settings using system properties This proxy selector
45 * provides backward compatibility with the old http protocol handler
46 * as far as how proxy is set
47 *
48 * Most of the implementation copied from the old http protocol handler
49 *
50 * Supports http/https/ftp.proxyHost, http/https/ftp.proxyPort,
51 * proxyHost, proxyPort, and http/https/ftp.nonProxyHost, and socks.
52 * NOTE: need to do gopher as well
53 */
54 public class DefaultProxySelector extends ProxySelector {
55
56 /**
57 * This is where we define all the valid System Properties we have to
58 * support for each given protocol.
59 * The format of this 2 dimensional array is :
60 * - 1 row per protocol (http, ftp, ...)
61 * - 1st element of each row is the protocol name
62 * - subsequent elements are prefixes for Host & Port properties
63 * listed in order of priority.
64 * Example:
65 * {"ftp", "ftp.proxy", "ftpProxy", "proxy", "socksProxy"},
66 * means for FTP we try in that oder:
67 * + ftp.proxyHost & ftp.proxyPort
68 * + ftpProxyHost & ftpProxyPort
69 * + proxyHost & proxyPort
70 * + socksProxyHost & socksProxyPort
71 *
72 * Note that the socksProxy should *always* be the last on the list
73 */
74 final static String[][] props = {
75 /*
76 * protocol, Property prefix 1, Property prefix 2, ...
77 */
78 {"http", "http.proxy", "proxy", "socksProxy"},
79 {"https", "https.proxy", "proxy", "socksProxy"},
80 {"ftp", "ftp.proxy", "ftpProxy", "proxy", "socksProxy"},
81 {"gopher", "gopherProxy", "socksProxy"},
82 {"socket", "socksProxy"}
83 };
84
85 private static final String SOCKS_PROXY_VERSION = "socksProxyVersion";
86
87 private static boolean hasSystemProxies = false;
88
89 static {
90 final String key = "java.net.useSystemProxies";
91 Boolean b = AccessController.doPrivileged(
92 new PrivilegedAction<Boolean>() {
93 public Boolean run() {
94 return NetProperties.getBoolean(key);
95 }});
96 if (b != null && b.booleanValue()) {
97 java.security.AccessController.doPrivileged(
98 new sun.security.action.LoadLibraryAction("net"));
99 hasSystemProxies = init();
100 }
101 }
102
103 /**
104 * How to deal with "non proxy hosts":
105 * since we do have to generate a RegexpPool we don't want to do that if
106 * it's not necessary. Therefore we do cache the result, on a per-protocol
107 * basis, and change it only when the "source", i.e. the system property,
108 * did change.
109 */
110
111 static class NonProxyInfo {
112 // Default value for nonProxyHosts, this provides backward compatibility
113 // by excluding localhost and its litteral notations.
114 static final String defStringVal = "localhost|127.*|[::1]";
115
116 String hostsSource;
117 RegexpPool hostsPool;
118 final String property;
119 final String defaultVal;
120 static NonProxyInfo ftpNonProxyInfo = new NonProxyInfo("ftp.nonProxyHosts", null, null, defStringVal);
121 static NonProxyInfo httpNonProxyInfo = new NonProxyInfo("http.nonProxyHosts", null, null, defStringVal);
122
123 NonProxyInfo(String p, String s, RegexpPool pool, String d) {
124 property = p;
125 hostsSource = s;
126 hostsPool = pool;
127 defaultVal = d;
128 }
129 }
130
131
132 /**
133 * select() method. Where all the hard work is done.
134 * Build a list of proxies depending on URI.
135 * Since we're only providing compatibility with the system properties
136 * from previous releases (see list above), that list will always
137 * contain 1 single proxy, default being NO_PROXY.
138 */
139 public java.util.List<Proxy> select(URI uri) {
140 if (uri == null) {
141 throw new IllegalArgumentException("URI can't be null.");
142 }
143 String protocol = uri.getScheme();
144 String host = uri.getHost();
145
146 if (host == null) {
147 // This is a hack to ensure backward compatibility in two
148 // cases: 1. hostnames contain non-ascii characters,
149 // internationalized domain names. in which case, URI will
150 // return null, see BugID 4957669; 2. Some hostnames can
151 // contain '_' chars even though it's not supposed to be
152 // legal, in which case URI will return null for getHost,
153 // but not for getAuthority() See BugID 4913253
154 String auth = uri.getAuthority();
155 if (auth != null) {
156 int i;
157 i = auth.indexOf('@');
158 if (i >= 0) {
159 auth = auth.substring(i+1);
160 }
161 i = auth.lastIndexOf(':');
162 if (i >= 0) {
163 auth = auth.substring(0,i);
164 }
165 host = auth;
166 }
167 }
168
169 if (protocol == null || host == null) {
170 throw new IllegalArgumentException("protocol = "+protocol+" host = "+host);
171 }
172 List<Proxy> proxyl = new ArrayList<Proxy>(1);
173
174 NonProxyInfo pinfo = null;
175
176 if ("http".equalsIgnoreCase(protocol)) {
177 pinfo = NonProxyInfo.httpNonProxyInfo;
178 } else if ("https".equalsIgnoreCase(protocol)) {
179 // HTTPS uses the same property as HTTP, for backward
180 // compatibility
181 pinfo = NonProxyInfo.httpNonProxyInfo;
182 } else if ("ftp".equalsIgnoreCase(protocol)) {
183 pinfo = NonProxyInfo.ftpNonProxyInfo;
184 }
185
186 /**
187 * Let's check the System properties for that protocol
188 */
189 final String proto = protocol;
190 final NonProxyInfo nprop = pinfo;
191 final String urlhost = host.toLowerCase();
192
193 /**
194 * This is one big doPrivileged call, but we're trying to optimize
195 * the code as much as possible. Since we're checking quite a few
196 * System properties it does help having only 1 call to doPrivileged.
197 * Be mindful what you do in here though!
198 */
199 Proxy p = AccessController.doPrivileged(
200 new PrivilegedAction<Proxy>() {
201 public Proxy run() {
202 int i, j;
203 String phost = null;
204 int pport = 0;
205 String nphosts = null;
206 InetSocketAddress saddr = null;
207
208 // Then let's walk the list of protocols in our array
209 for (i=0; i<props.length; i++) {
210 if (props[i][0].equalsIgnoreCase(proto)) {
211 for (j = 1; j < props[i].length; j++) {
212 /* System.getProp() will give us an empty
213 * String, "" for a defined but "empty"
214 * property.
215 */
216 phost = NetProperties.get(props[i][j]+"Host");
217 if (phost != null && phost.length() != 0)
218 break;
219 }
220 if (phost == null || phost.length() == 0) {
221 /**
222 * No system property defined for that
223 * protocol. Let's check System Proxy
224 * settings (Gnome & Windows) if we were
225 * instructed to.
226 */
227 if (hasSystemProxies) {
228 String sproto;
229 if (proto.equalsIgnoreCase("socket"))
230 sproto = "socks";
231 else
232 sproto = proto;
233 Proxy sproxy = getSystemProxy(sproto, urlhost);
234 if (sproxy != null) {
235 return sproxy;
236 }
237 }
238 return Proxy.NO_PROXY;
239 }
240 // If a Proxy Host is defined for that protocol
241 // Let's get the NonProxyHosts property
242 if (nprop != null) {
243 nphosts = NetProperties.get(nprop.property);
244 synchronized (nprop) {
245 if (nphosts == null) {
246 if (nprop.defaultVal != null) {
247 nphosts = nprop.defaultVal;
248 } else {
249 nprop.hostsSource = null;
250 nprop.hostsPool = null;
251 }
252 }
253 if (nphosts != null) {
254 if (!nphosts.equals(nprop.hostsSource)) {
255 RegexpPool pool = new RegexpPool();
256 StringTokenizer st = new StringTokenizer(nphosts, "|", false);
257 try {
258 while (st.hasMoreTokens()) {
259 pool.add(st.nextToken().toLowerCase(), Boolean.TRUE);
260 }
261 } catch (sun.misc.REException ex) {
262 }
263 nprop.hostsPool = pool;
264 nprop.hostsSource = nphosts;
265 }
266 }
267 if (nprop.hostsPool != null &&
268 nprop.hostsPool.match(urlhost) != null) {
269 return Proxy.NO_PROXY;
270 }
271 }
272 }
273 // We got a host, let's check for port
274
275 pport = NetProperties.getInteger(props[i][j]+"Port", 0).intValue();
276 if (pport == 0 && j < (props[i].length - 1)) {
277 // Can't find a port with same prefix as Host
278 // AND it's not a SOCKS proxy
279 // Let's try the other prefixes for that proto
280 for (int k = 1; k < (props[i].length - 1); k++) {
281 if ((k != j) && (pport == 0))
282 pport = NetProperties.getInteger(props[i][k]+"Port", 0).intValue();
283 }
284 }
285
286 // Still couldn't find a port, let's use default
287 if (pport == 0) {
288 if (j == (props[i].length - 1)) // SOCKS
289 pport = defaultPort("socket");
290 else
291 pport = defaultPort(proto);
292 }
293 // We did find a proxy definition.
294 // Let's create the address, but don't resolve it
295 // as this will be done at connection time
296 saddr = InetSocketAddress.createUnresolved(phost, pport);
297 // Socks is *always* the last on the list.
298 if (j == (props[i].length - 1)) {
299 int version = NetProperties.getInteger(SOCKS_PROXY_VERSION, 5).intValue();
300 return SocksProxy.create(saddr, version);
301 } else {
302 return new Proxy(Proxy.Type.HTTP, saddr);
303 }
304 }
305 }
306 return Proxy.NO_PROXY;
307 }});
308
309 proxyl.add(p);
310
311 /*
312 * If no specific property was set for that URI, we should be
313 * returning an iterator to an empty List.
314 */
315 return proxyl;
316 }
317
318 public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
319 if (uri == null || sa == null || ioe == null) {
320 throw new IllegalArgumentException("Arguments can't be null.");
321 }
322 // ignored
323 }
324
325
326 private int defaultPort(String protocol) {
327 if ("http".equalsIgnoreCase(protocol)) {
328 return 80;
329 } else if ("https".equalsIgnoreCase(protocol)) {
330 return 443;
331 } else if ("ftp".equalsIgnoreCase(protocol)) {
332 return 80;
333 } else if ("socket".equalsIgnoreCase(protocol)) {
334 return 1080;
335 } else if ("gopher".equalsIgnoreCase(protocol)) {
336 return 80;
337 } else {
338 return -1;
339 }
340 }
341
342 private native static boolean init();
343 private native Proxy getSystemProxy(String protocol, String host);
344 }